package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.controller.param.ShowWinningRecordsParam;
import com.example.lotterysystem.dao.dataobject.*;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.dto.WinningRecordsResultDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {

    private static final Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);

    private final Long WINNING_RECORD_TIMEOUT = 60*60*24*2L;
    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeParam param) {
        Map<String,String> map = new HashMap<>();
        map.put("messageId",String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        //将消息发送给哪一个交换机，还要知道key和消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);
        logger.info("mq发送消息成功，map：{}",JacksonUtil.writeValueAsString(map));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
        //校验活动是否存在
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        //校验奖品是否存在
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(),param.getPrizeId());
        if (activityDO == null || activityPrizeDO == null){
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_NOT_EXIST);
            logger.info("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_NOT_EXIST.getMsg());
            return false;
        }
        //校验活动是否有效
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_STATUS_ERROR);
            logger.info("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_STATUS_ERROR.getMsg());
            return false;
        }
        //校验活动关联奖品是否有效
        if (activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_STATUS_ERROR);
            logger.info("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_PRIZE_STATUS_ERROR.getMsg());
            return false;
        }
        //中奖者人数和奖品数量是否一致
        if (activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()){
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            logger.info("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR.getMsg());
            return false;
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        // 查询相关信息：活动，人员，奖品，活动关联奖品表(查询4个表)
        ActivityDO activityDO=activityMapper.selectById(param.getActivityId());
        List<UserDO> userDOList=userMapper.batchSelectByIds(
                param.getWinnerList()
                        .stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        PrizeDO prizeDO=prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        // 构造中奖记录，保存
        List<WinningRecordDO> winningRecordDOList = userDOList.stream()
                .map(userDO -> {
                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                    winningRecordDO.setActivityId(activityDO.getId());
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    winningRecordDO.setPrizeId(prizeDO.getId());
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecordDO.setWinnerId(userDO.getId());
                    winningRecordDO.setWinnerEmail(userDO.getEmail());
                    winningRecordDO.setWinnerName(userDO.getUserName());
                    winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecordDO.setWinningTime(param.getWinningTime());
                    return winningRecordDO;
                }).collect(Collectors.toList());
        winningRecordMapper.batchInsert(winningRecordDOList);
        //缓存中奖记录
        //1.缓存奖品维度中奖记录(单个中奖记录)(key:WinningRecord_activityId_prizeId,value:winningRecordDOList(奖品维度的中奖名单))
        cacheWinningRecord(param.getActivityId()+"_"+param.getPrizeId(),winningRecordDOList,WINNING_RECORD_TIMEOUT);
        //2.缓存活动维度中奖记录(一个活动的全部中奖记录)(key:WinningRecord_activityId,value:winningRecordDOList(活动维度的中奖名单))
        //要等抽奖活动完成后才能缓存
        List<WinningRecordDO> allList = winningRecordMapper.selectByActivityId(param.getActivityId());
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            cacheWinningRecord(String.valueOf(param.getActivityId()),
                    allList,
                    WINNING_RECORD_TIMEOUT);
        }
        return winningRecordDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if (activityId == null){
            logger.warn("要删除的中奖记录的活动id为空");
            return;
        }
        //删除数据库表
        winningRecordMapper.deleteRecords(activityId,prizeId);
        //从缓存中删除数据(奖品维度和活动维度)
        if (prizeId != null){
            deleteWinningRecords(activityId+"_"+prizeId);
        }
        //无论是否传递了prizeId，我们都要将缓存中的活动信息删除
        //如果传递了prizeId，说明奖品为被抽完，说明此时抽奖活动还没结束
        //如果没传递prizeId，就只是删除缓存中的活动信息
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinningRecordsResultDTO> getWinningRecords(ShowWinningRecordsParam param) {
        //1.先查redis
        String key = param.getPrizeId() == null
                ? String.valueOf(param.getActivityId())
                : param.getActivityId()+"_"+param.getPrizeId();
        List<WinningRecordDO> winningRecordDOList = getWinningRecords(key);
        if (!CollectionUtils.isEmpty(winningRecordDOList)){
            return convertToWinningRecordsResultDTO(winningRecordDOList);
        }
        //2.redis中不存在，查数据库表
        winningRecordDOList=winningRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(),param.getPrizeId());
        //3.缓存到redis中
        if (CollectionUtils.isEmpty(winningRecordDOList)){
            logger.info("查询的中奖记录为空！param:{}",param);
            return Arrays.asList();
        }
        cacheWinningRecord(key,winningRecordDOList,WINNING_RECORD_TIMEOUT);
        return convertToWinningRecordsResultDTO(winningRecordDOList);
    }

    private List<WinningRecordsResultDTO> convertToWinningRecordsResultDTO(
            List<WinningRecordDO> winningRecordDOList) {
        if (CollectionUtils.isEmpty(winningRecordDOList)){
            return Arrays.asList();
        }
        return winningRecordDOList.stream()
                .map(winningRecordDO -> {
                    WinningRecordsResultDTO recordsResultDTO = new WinningRecordsResultDTO();
                    recordsResultDTO.setWinnerId(winningRecordDO.getWinnerId());
                    recordsResultDTO.setWinnerName(winningRecordDO.getWinnerName());
                    recordsResultDTO.setPrizeName(winningRecordDO.getPrizeName());
                    recordsResultDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(
                            winningRecordDO.getPrizeTier()));
                    recordsResultDTO.setWinningTime(winningRecordDO.getWinningTime());
                    return recordsResultDTO;
                }).collect(Collectors.toList());
    }

    /**
     * 从缓存中删除中奖记录
     * @param key
     */
    private void deleteWinningRecords(String key) {

        try {
            if (redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)){
                //存在再删除
                redisUtil.delete(WINNING_RECORDS_PREFIX+key);
            }
        }catch (Exception e){
            logger.error("删除中奖记录缓存异常，key：{}",key);
        }

    }

    /**
     * 缓存中奖记录
     * @param key
     * @param winningRecordDOList
     * @param time
     */
    private void cacheWinningRecord(String key,
                                    List<WinningRecordDO> winningRecordDOList,
                                    Long time) {
        String str="";
        try {
            if (StringUtils.hasText(key)
                    || CollectionUtils.isEmpty(winningRecordDOList)){
                logger.warn("要缓存的内容为空！key:{},value:{}",WINNING_RECORDS_PREFIX+key,
                        JacksonUtil.writeValueAsString(winningRecordDOList));
            }
            str = JacksonUtil.writeValueAsString(winningRecordDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX+key, str, time);
        }catch (Exception e){
            logger.error("缓存中奖记录异常！key:{},value:{}",WINNING_RECORDS_PREFIX+key,str);
        }
    }

    /**
     * 从缓存中获取中奖记录
     * @param key
     * @return
     */
    private List<WinningRecordDO> getWinningRecords(String key){

        try {
            if (!StringUtils.hasText(key)){
                logger.warn("要从缓存中获取中奖记录的key为空！");
                return Arrays.asList();
            }
            String str=redisUtil.get(WINNING_RECORDS_PREFIX+key);
            if (!StringUtils.hasText(str)){
                return Arrays.asList();
            }
            List<WinningRecordDO> winningRecordDOList = JacksonUtil.readListValue(str,WinningRecordDO.class);
            return winningRecordDOList;
        }catch (Exception e){
            logger.error("从缓存中查询中奖记录异常！key:{}",WINNING_RECORDS_PREFIX+key);
            return Arrays.asList();
        }

    }

}
